package org.sixtynine.stock.controller.front;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutionException;

import org.sixtynine.stock.Constants;
import org.sixtynine.stock.ReturnData;
import org.sixtynine.stock.cache.CompanyCache;
import org.sixtynine.stock.cache.CompanyDto;
import org.sixtynine.stock.cache.CurrentShareCache;
import org.sixtynine.stock.controller.AbstractFrontController;
import org.sixtynine.stock.dto.Page;
import org.sixtynine.stock.entity.BaseEntity;
import org.sixtynine.stock.entity.ChartData;
import org.sixtynine.stock.entity.IntradayShareData;
import org.sixtynine.stock.util.ChartDataFactory;
import org.sixtynine.stock.util.StockComparator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

/**
 * the controller which feed data for charts
 * 
 * @author Thilanka
 * 
 */
@Controller
public class ChartsController extends AbstractFrontController implements
		ChartsPage {

	/** number of results per page */
	private static final int RESULT_COUNT = 6;

	@Autowired
	CompanyCache companyCache;

	@Autowired
	CurrentShareCache currentShareCache;

	@RequestMapping(value = "/chart/main")
	public ModelAndView showResults(
			@RequestParam(value = "page", required = false, defaultValue = "1") int page,
			@RequestParam(value = "code", required = false) String code,
			@RequestParam(value = "name", required = false) String name)
			throws ExecutionException {
		ModelAndView modelAndView = new ModelAndView("/chart/main");

		ReturnData<List<String>> companyReturnData = getCompanyCodeList(page,
				code, name);
		List<String> companyList = companyReturnData.getData();

		int startIndex = (page - 1) * RESULT_COUNT;
		Page<String> pageData = new Page<String>(companyReturnData.getNo(),
				startIndex, companyList.size(), RESULT_COUNT, companyList);

		modelAndView.addObject("data", pageData);
		showBannar(modelAndView);
		return modelAndView;
	}

	private ReturnData<List<String>> getCompanyCodeList(int page, String code,
			String name) throws ExecutionException {
		List<CompanyDto> companies = companyCache
				.get(Constants.COMPANY_CACHED_DATA);

		List<CompanyDto> filtedCompanies = new ArrayList<CompanyDto>();

		ReturnData<List<String>> returnData = new ReturnData<List<String>>(
				Constants.SUCCESS, "");

		List<String> companyList = new ArrayList<String>();
		for (CompanyDto companyDto : companies) {
			if (companyDto.getDailyShareData().size() > 0) {
				filtedCompanies.add(companyDto);
				continue;
			}
		}

		if (code != null) {
			for (CompanyDto companyDto : filtedCompanies) {
				if (companyDto.getCompany().getCode().equals(code)) {
					companyList.add(companyDto._getShortCompanyCode());
					break;
				}
			}
			returnData.setNo(companyList.size());
		} else if (name != null) {
			for (CompanyDto companyDto : filtedCompanies) {
				if (companyDto.getCompany().getName().equals(name)) {
					companyList.add(companyDto._getShortCompanyCode());
					break;
				}
			}

			returnData.setNo(companyList.size());
		} else {

			int startIndex = (page - 1) * RESULT_COUNT;
			int endIndex = startIndex + RESULT_COUNT;
			endIndex = endIndex > filtedCompanies.size() ? filtedCompanies
					.size() : endIndex;

			List<CompanyDto> pageData = filtedCompanies.subList(startIndex,
					endIndex);
			for (CompanyDto companyDto : pageData) {
				companyList.add(companyDto._getShortCompanyCode());
			}
			returnData.setNo(companies.size());
		}

		returnData.setData(companyList);
		return returnData;
	}

	@RequestMapping(value = "/charts/comData", method = RequestMethod.GET)
	@ResponseBody
	public List<String> getCompData(
			@RequestParam(value = "page", required = false, defaultValue = "1") int page,
			@RequestParam(value = "code", required = false) String code,
			@RequestParam(value = "name", required = false) String name)
			throws ExecutionException {
		// passs the criteria
		return getCompanyCodeList(page, code, name).getData();
	}

	@RequestMapping(value = "/charts/list", method = RequestMethod.GET)
	@ResponseBody
	public String getData(
			@RequestParam(value = "code", required = false) String code,
			@RequestParam(value = "type", required = false) String type,
			@RequestParam(value = "timeRange", required = false) String timeRange,
			@RequestParam(value = "showVolume", required = false) Boolean showVolume)
			throws ExecutionException {

		List<CompanyDto> companies = companyCache
				.get(Constants.COMPANY_CACHED_DATA);

		CompanyDto selectedCompany = null;
		for (CompanyDto companyDto : companies) {
			if (companyDto._getShortCompanyCode().equals(code)) {
				selectedCompany = companyDto;
				break;
			}
		}

		StringBuilder data = new StringBuilder();
		if (timeRange != null && timeRange.equals(TIME_RANGE_INTRADAY)) {
			List<IntradayShareData> intradayShareDatas = currentShareCache
					.getDailyShare(selectedCompany._getCompanyCode());
			if (intradayShareDatas == null) {
				return "";
			}

			Collections.sort(intradayShareDatas,
					(Comparator<IntradayShareData>) StockComparator
							.getInstance(StockComparator.INTRADAY_TIME_CMP,
									null));
			IntradayShareData openingShare = currentShareCache
					.getOpeningShare(selectedCompany._getCompanyCode());

			if (openingShare == null) {
				return "";
			}

			data.append("[");
			// iterate from other way round
			for (int i = (intradayShareDatas.size() - 1); i >= 0; i--) {
				IntradayShareData intradayShareData = (IntradayShareData) intradayShareDatas
						.get(i);
				data.append("[").append(intradayShareData.getTime().getTime());

				if (type != null && type.equals(CHART_TYPE_CANDLESTICK)) {
					data.append(DATA_SEPERATOR)
							.append(openingShare.getLastTradedPrice())
							.append(DATA_SEPERATOR)
							.append(intradayShareData.getHigh());
					data.append(DATA_SEPERATOR)
							.append(intradayShareData.getLow())
							.append(DATA_SEPERATOR)
							.append(intradayShareData.getLastTradedPrice());
				} else {
					data.append(DATA_SEPERATOR).append(
							intradayShareData.getLastTradedPrice());
				}
				data.append("]");
				data.append(DATA_SEPERATOR);
			}
			if (data.indexOf(DATA_SEPERATOR) > 0) {
				data.deleteCharAt(data.lastIndexOf(DATA_SEPERATOR));
			}
			data.append("]");

		} else {
			List<BaseEntity> historicalShareData = ChartDataFactory
					.getChartData(selectedCompany, timeRange);

			data.append("[");
			// iterate from other way round
			for (int i = (historicalShareData.size() - 1); i >= 0; i--) {
				ChartData chartData = (ChartData) historicalShareData.get(i);

				data.append("[").append(chartData.getDate().getTime());
				if (type != null && type.equals(CHART_TYPE_CANDLESTICK)) {
					data.append(DATA_SEPERATOR)
							.append(chartData.getOpeningPrice())
							.append(DATA_SEPERATOR).append(chartData.getHigh());
					data.append(DATA_SEPERATOR).append(chartData.getLow())
							.append(DATA_SEPERATOR)
							.append(chartData.getSharePrice());
					if (showVolume) {
						data.append(DATA_SEPERATOR).append(
								chartData.getVolume());
					}
				} else {
					data.append(DATA_SEPERATOR).append(
							chartData.getSharePrice());
					if (showVolume) {
						data.append(DATA_SEPERATOR).append(
								chartData.getVolume());
					}
				}
				data.append("]");
				data.append(DATA_SEPERATOR);
			}
			if (data.indexOf(DATA_SEPERATOR) > 0) {
				data.deleteCharAt(data.lastIndexOf(DATA_SEPERATOR));
			}
			data.append("]");
		}

		return data.toString();

	}

}
